package com.jbm;

import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * Created by root on 17-3-3.
 */
public class ConnectAgent implements Life
{
	public static final char CA_Type_Json_Server = 0;
	public static final char CA_Type_Json_Client = 1;
	public static final char CA_Type_GD_Server = 2;

	protected final Object m_connect_mutex = new Object();
	protected final Object m_status_mutex = new Object();

	protected boolean m_connectisclose;

	protected int m_sessiontimeout;

	private boolean m_reconnect;

	private NioSocketConnector m_connector;
	private ConnectFuture m_cf;

	public int m_device_id;

	public int m_type;

	public String m_remotehost;
	public int m_remoteport;
	public int m_localport;

	public String wy_code;

	public ConnectAgent()
	{
		super();

		m_connectisclose = false;

		m_status = life_status_newlife;
		m_isasked = false;
		m_resend = true;

		m_iosession = null;

		m_reconnect = true;
	}

	public ConnectAgent(int device_id, String remotehost, int remoteport, int localport, int type)
	{
		this();

		m_device_id = device_id;
		m_remotehost = remotehost;
		m_remoteport = remoteport;
		m_localport = localport;
		m_type = type;
	}

	public ConnectAgent(int device_id, String remotehost, int remoteport, int localport, int type, int timeout)
	{
		this(device_id,remotehost,remoteport,localport,type);

		m_sessiontimeout = timeout;
	}

	private void Send1st(IoSession ios)
	{
		String request = HttpHandler.BuildHello(this.wy_code);

		ios.write(request);
	}

	public boolean ConnectServer( )
	{
		synchronized (m_connect_mutex)
		{
			m_reconnect = true;

			if ( m_connectisclose )
				return false;

			if (m_iosession == null || !m_iosession.isConnected())
			{
				m_connector = new NioSocketConnector();
				m_connector.setConnectTimeoutMillis(m_sessiontimeout);
				m_connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new JSONCodeFactory("UTF-8")));
				m_connector.setDefaultRemoteAddress(new InetSocketAddress(m_remotehost, m_remoteport));
				SocketSessionConfig cfg = m_connector.getSessionConfig();
				cfg.setUseReadOperation(true);

				m_connector.setHandler(new BridgeHnadler());

				m_connector.getFilterChain().addFirst("reconnection", new IoFilterAdapter()
				{
					@Override
					public void sessionClosed(NextFilter nextFilter, IoSession iosession) throws Exception
					{
						for (; m_reconnect; )
						{
							SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG,"Reconnect...");
							try
							{
								m_cf = m_connector.connect();

								m_cf.awaitUninterruptibly();

								m_iosession = m_cf.getSession();// 获取会话
								if (m_iosession.isConnected())
								{
									m_iosession.setAttribute("CA_Key", ConnectAgent.this);
									m_trytimes = 0;

									Send1st(m_iosession);

									SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG, "Reconnect OK");

									break;
								} else
									SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG, "Reconnect Try again");
							} catch (Exception ex)
							{
								SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG,"Reconnect Failed");
							}

							if ( m_trytimes >= m_retry )
								m_trytimes = m_retry;

							Thread.sleep(3000);
						}
					}
				});

				m_cf = m_connector.connect ( );
				m_cf.awaitUninterruptibly ( );

				m_iosession = m_cf.getSession();

				m_iosession.setAttribute("CA_Key",this);

				Send1st(m_iosession);

				return true;
			}

			return true;
		}
	}

	public void DisconnectServer( boolean reconnect )
	{
		if ( m_iosession != null && m_iosession.isConnected() )
		{
			m_reconnect = reconnect;

			m_iosession.close(false);
			m_iosession = null;
		}
	}

	public boolean ReConnectP2P(IoSession ios)
	{
		return false;
	}

	public boolean Send ( String open )
	{
		if ( m_iosession != null )
		{
			WriteFuture wf = m_iosession.write(open);
			try
			{
				wf.await();
			}catch (Exception e)
			{
			}

			return true;
		}

		return false;
	}

	public boolean Send ( byte[] open )
	{
		if ( m_iosession != null )
		{
			WriteFuture wf = m_iosession.write(open);
			try
			{
				wf.await();
			}catch (Exception e)
			{
			}

			return true;
		}

		return false;
	}

	public int getM_type()
	{
		return m_type;
	}

	public void setM_connectisclose(boolean isclose )
	{
		m_connectisclose = isclose;
	}

	protected int m_retry;
	protected int m_timeout;
	protected int m_status;
	protected Resource m_resource;
	protected ResKey m_lifename;
	protected Object[] m_param;
	protected long m_borntimems;
	protected long m_lasttryms;
	protected int m_trytimes;

	protected IoSession m_iosession;

	protected boolean m_isasked;
	protected boolean m_resend;
	protected boolean m_keepalive;

	public ResKey getM_lifename()
	{
		return m_lifename;
	}

	public void setM_iosession (IoSession iosession)
	{
		m_iosession = iosession;
	}

	public IoSession getM_iosession()
	{
		return m_iosession;
	}

	public void setM_keepalive(boolean keepalive)
	{
		m_keepalive = keepalive;
	}

	public void Break( boolean offline )
	{
		synchronized (m_status_mutex)
		{
			if ( m_status != life_status_close && m_status != life_status_svr_break )
				m_status = life_status_svr_break;
			else
				return;
		}

		if ( offline )
			DisconnectServer(false);
	}

	@Override
	public void Born(int retry, int timeout)
	{
		m_borntimems = System.currentTimeMillis();
		m_lasttryms = m_borntimems;
		m_timeout = timeout;

		m_retry = retry;
		m_trytimes = 0;
	}

	@Override
	public boolean AmILiving()
	{
		return true;
	}

	@Override
	public boolean IamLiving()
	{
		if ( m_type == CA_Type_GD_Server )
			m_lasttryms = System.currentTimeMillis();
		else
			m_trytimes = 0;
		return true;
	}

	@Override
	public boolean RuLiving()
	{
		return true;
	}

	@Override
	public boolean RuLivingWell()
	{
		return true;
	}

	@Override
	public boolean Wakeup(Object... args)
	{
		long cms = System.currentTimeMillis();

		if ( m_lasttryms + m_timeout < cms )
		{
			if ( m_type == CA_Type_Json_Server ||
					m_type == CA_Type_Json_Client )
			{
				m_lasttryms = cms;

				if (m_keepalive)
				{
					if ( m_trytimes >= m_retry )
					{
						m_trytimes++;
						if ( m_iosession.isConnected() )
						{
							SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG, "Timeout Session Close...");
							m_iosession.close(false);
							SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG, "Timeout Session Closed");
						}

						if ( m_type == CA_Type_Json_Client &&
								m_trytimes - m_retry > 3 )
						{
							SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG, "New Connection...");
							ConnectAgent ca = null;
							try
							{
								ca = new ConnectAgent(0,
										InetAddress.getByName("app.jiabangmang.com").getHostAddress(),
										1990, 0, ConnectAgent.CA_Type_Json_Client, 2000);
							} catch (Exception e)
							{
								SysLog.g_syslog.MakeLog(SysLog.SYS_LOG_LEVEL_DEBUG, "NC Failed...");
								return true;
							}

							ca.wy_code = wy_code;
							ca.Born(3, 5000);
							ca.ConnectServer();
							ca.setM_keepalive(true);

							Die();

							BridgeHnadler.b_client_ca.put(wy_code,ca);
							BridgeHnadler.b_pool.Add(ca);
						}
					}else
					{
						m_trytimes++;

						String request = HttpHandler.BuildKeepalive();

						Send(request);
					}
				}
			}else
				Die();
		}

		return true;
	}

	@Override
	public int IsLast ( Object... args )
	{
		return 0;
	}

	@Override
	public boolean Die()// server三种结束方式 1:主动切断由IoSession结束引发的. 2:Session超时引发的 3:同种客户端同时登录前一个被顶掉的(method Break)
	{
		synchronized (m_status_mutex)
		{
			if ( m_status != life_status_close && m_status != life_status_svr_break )
				m_status = life_status_close;
			else
				return false;
		}

		ReleaseFromPool();

		if ( m_iosession != null && m_iosession.isConnected() )
		{
			m_iosession.close(false);
		}

		return true;
	}

	@Override
	public boolean BindParameter(Object... param)
	{
		m_param = param;
		return true;
	}

	@Override
	public Object GetParameter(int index)
	{
		return m_param[index];
	}

	@Override
	public boolean BindLifeName(ResKey key)
	{
		m_lifename = key;
		return true;
	}

	@Override
	public ResKey GetLifeName()
	{
		return m_lifename;
	}

	@Override
	public boolean SetStatus(int status)
	{
		synchronized ( m_status_mutex )
		{
			m_status = status;
			return true;
		}
	}

	@Override
	public int GetStatus()
	{
		return m_status;
	}

	@Override
	public int GetOverTime()
	{
		return m_timeout;
	}

	@Override
	public boolean ReleaseFromPool ( )
	{
		if ( m_resource != null )
			return m_resource.Drop(m_lifename);
		else
			return false;
	}

	@Override
	public boolean BindPool ( Resource resource )
	{
		m_resource = resource;
		return true;
	}

	@Override
	public Object[] GetUniqueIndex ()
	{
		Object[] rtn = new Object[1];
		rtn[0] = m_remotehost;
		return rtn;
	}

	@Override
	public void SetUniqueIndex ( Object... uniqueidx )
	{
	}
}
